package org.amos.filestorage.platform.ftp;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.ssh.JschRuntimeException;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import org.amos.filestorage.exception.FileStorageException;
import org.amos.filestorage.platform.AbstractFilePlatform;
import org.amos.filestorage.platform.config.SftpFilePlatformConfig;
import org.junit.internal.Throwables;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;

import static com.jcraft.jsch.ChannelSftp.SSH_FX_NO_SUCH_FILE;

/**
 * SFTP 存储
 */
public class SftpFilePlatform extends AbstractFilePlatform<SftpFilePlatformConfig> {

    private Sftp client;

    public SftpFilePlatform(String id, SftpFilePlatformConfig config) {
        super(id, config);
    }

    @Override
    protected void initClient() {
        Session session = null;
        try {
            if (StrUtil.isNotBlank(config.getPrivateKeyPath())) {
                //使用秘钥连接，这里手动读取 byte 进行构造用于兼容Spring的ClassPath路径、文件路径、HTTP路径等
                byte[] passphrase = StrUtil.isBlank(config.getPassword()) ? null : config.getPassword().getBytes(StandardCharsets.UTF_8);
                JSch jsch = new JSch();
                byte[] privateKey = IoUtil.readBytes(URLUtil.url(config.getPrivateKeyPath()).openStream());
                jsch.addIdentity(config.getPrivateKeyPath(), privateKey, null, passphrase);
                session = JschUtil.createSession(jsch, config.getHost(), config.getPort(), config.getUser());
                session.connect((int) config.getConnectionTimeout());
            } else {
                session = JschUtil.openSession(config.getHost(), config.getPort(), config.getUser(), config.getPassword(), (int) config.getConnectionTimeout());
            }
            client = new Sftp(session, Charset.forName(config.getCharset()), config.getConnectionTimeout());
        } catch (Exception e) {
            JschUtil.close(session);
            throw new FileStorageException("SFTP连接失败！");
        }
    }

    @Override
    public String upload(byte[] bytes, String path, String type) {
        try {
            String absolutePath = getAbsolutePath(path);
            if (!client.exist(path)) {
                client.mkDirs(path);
            }
            client.upload(absolutePath, FileUtil.getName(absolutePath), new ByteArrayInputStream(bytes));
            return "";
        } catch (JschRuntimeException e) {
            throw new FileStorageException(Throwables.getStacktrace(e));
        } finally {
            IoUtil.close(client);
        }
    }

    @Override
    public Boolean delete(String path) {
        try {
            delFile(client, getAbsolutePath(path));
            return Boolean.TRUE;
        } catch (JschRuntimeException e) {
            throw new FileStorageException(Throwables.getStacktrace(e));
        }
    }

    public void delFile(Sftp client, String filename) {
        try {
            client.delFile(filename);
        } catch (JschRuntimeException e) {
            if (!(e.getCause() instanceof SftpException && ((SftpException) e.getCause()).id == SSH_FX_NO_SUCH_FILE)) {
                throw e;
            }
        }
    }


    @Override
    public Boolean exists(String path) {
        try {
            return client.exist(getAbsolutePath(path));
        } catch (JschRuntimeException e) {
            throw new FileStorageException(Throwables.getStacktrace(e));
        }
    }

    @Override
    public void download(String path, Consumer<InputStream> consumer) {
        try (InputStream in = client.getClient().get(getAbsolutePath(path))) {
            consumer.accept(in);
        } catch (IOException | JschRuntimeException | SftpException e) {
            throw new FileStorageException(org.junit.internal.Throwables.getStacktrace(e));
        }
    }

    /**
     * 获取远程绝对路径
     */
    public String getAbsolutePath(String path) {
        return config.getDomain() + path;
    }

}
